<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
    <script>
        //操作 DOM： 创建 4 个 p 元素，内容是 “测试”
        // const data = [100,400,310,10]
        // d3.select('body') // 用于获取元素
        // .selectAll('p') // p 标签可以不存在
        // .data(data) // 把数据绑定到 p 元素上
        // .enter()
        // .append('p') // 创建元素
        // .text('测试') // 元素 text 属性
    </script>

    <!-- <div id="app">
        <p> p 元素</p>
        <p> p 元素</p>
        <p> p 元素</p>
    </div>
    <div>x</div>
    <svg width="400" height="300">
        <rect x="100" y="100" width="200" height="200" style="stroke:blue;stroke-width:4"></rect>
    </svg>
    <script>
        // 一、操作元素
        // 1.选中元素（select、selectAll）；
        // 2.操作元素本质是操作元素的属性值，所以要先获取元素属性（attr）；
        // 3.修改属性，让界面变化（attr、style）；
        // 4.更新界面元素，需要执行元素的增删（append、remove）；
        
        // 1.选中元素
        // console.log(d3.select('#app')) // 返回的是数组
        // console.log(d3.select('#app p')) // 返回值是数组，但是只有中一个
        // console.log(d3.selectAll('#app p')) // 可以选中  #app 下面所有的 p

        // 2.获取属性
        console.log(d3.select('rect').attr('width')) // 字符 300
        console.log(+d3.select('rect').attr('width')) // 转换为数值

        // 3.设置属性
        d3.select('rect')
            .attr('fill','orange')
            .attr('transform','translate(100,100)')

        // 3.更新界面元素
        // 添加元素
        d3.select('svg')
          .append('rect') // 在 svg 中添加 rect
          .attr('x','100')
          .attr('y','10')
          .attr('width','400')
          .attr('height','40')
          .attr('fill','lightgreen')
        d3.select('svg').append('text')
           .attr('x','100')
           .attr('y','280')
           .attr('fill','red')
           .attr('font-size',16)
           .attr('textLength',50)
           .text('测试') 
        // 删除元素
        d3.selectAll('rect').remove()

    </script> -->

      <!-- <p>p1</p>
      <p>p2</p>
      <p>p3</p>
     <script>
        // 二、数据绑定：data（自动遍历）、datum（完整的数据）
        d3.select('body').append('svg')
            .attr('width', 600)
            .attr('height', 400)

        // 绘制多个圆形
        // d3.select('svg')
        //     .append('circle')
        //     .attr('cx', '100')
        //     .attr('cy', '100')
        //     .attr('r', '50')
        //     .attr('fill', 'lightgreen')
        // d3.select('svg')
        //     .append('circle')
        //     .attr('cx', '300')
        //     .attr('cy', '100')
        //     .attr('r', '50')
        //     .attr('fill', 'lightblue')
        // 使用数据绑定
        const data = [
            { cx: 100, cy: 250, r: 50, fill: 'lightgreen' },
            { cx: 200, cy: 250, r: 50, fill: 'lightcyan' },
            { cx: 300, cy: 250, r: 50, fill: 'lightblue' },
        ]
        d3.select('svg').selectAll('circle')
          .data(data)
          .enter() //  enter 选择集
          .append('circle')
          .attr('cx',d => d.cx)
          .attr('cy',d => d.cy)
          .attr('r', d => d.r)
          .attr('fill', d => d.fill)

        d3.selectAll('p')
        .datum([1,2,3,4,5])
        .text(d=>d)
    </script> -->


    <!-- <p>哈哈哈</p>
    <p>哈哈哈</p>
    <p>哈哈哈</p>
    <a>a1</a>
    <a>a2</a>
    <a>a3</a>
    <a>a4</a>
    <a>a5</a>
    <a>a6</a>
    <a>a7</a>
    <script>
        // 三、选择集（enter、update、exit）
        // d3 有三种选择集：
        //  enter（获取有数据，但是没有元素的选择器）对应 新增
        //  update（元素和数据是一一对应的）对应 修改
        //  exit（获取有元素，但是没有数据的选择器）对应 删除

        // 界面元素：  rect rect rect rect
        // 数据    ：   d1   d2   d3   d4    d5    d6    d7
        // 此时 update 得到 [d1,d2,d3,d4,d5] 对应的 rect 
        //      enter 得到 [d5,d6,d7]  对应需要创建的 rect 

        // 界面元素：  rect1 rect2 rect3 rect4 rect5 rect6
        // 数据    ：   d1   d2   d3
        // 此时 update 得到 [rect1,rect2,rect3]
        //      exit 得到 [rect4,rect5,rect6]

        const  data = [1,2,3,4,5]
        const allP = d3.selectAll('body p') // 返回 update  选择集
        
        // 更新元素
        const  update1 = allP.data(data)
        update1.text(d => '更新'+d)
        
        // 新增
        const enter = update1.enter()
        enter.append('p').text(d=>'新增'+d)

        // 删除多余的 4个
        d3.selectAll('body a').data([1,2,3])
        .exit()
        .remove()
    </script> -->

    <!-- <div id="app"></div>
    <style>
        #app{
            margin: 40px auto 0px;
            border: 1px solid red;
            max-width: calc(100% - 10px);
        }
    </style>
    <script>
        // 四、demo：绘制直方图
        const  width = 700
        const height = 400
        const rectStep = 40 // 间距
        const rectWidth = 30 // 宽度
        const  data = [64,20,90,87,65,32,10,47]
        const margin = {left:20,right:20,top:20,bottom:20}
        d3.select('#app').append('svg')
            .attr('width',width)
            .attr('height',height)

        d3.select('svg').selectAll('rect')
            .data(data)
            .enter()
            .append('rect')
            .attr('x',(d,i)=>margin.left + i*rectStep)
            .attr('y',(d)=>height - d - margin.bottom )
            .attr('width',rectWidth)
            .attr('height',d=>d)
            .attr('fill','lightblue')

        //  文字
        d3.select('svg').selectAll('text')
            .data(data)
            .enter('text')
            .append('text')
            .attr('fill','#333')
            .attr('font-size',18)
            .attr('x',(d,i)=>margin.left + i*rectStep)
            .attr('y',(d)=>height - d - margin.bottom - 5)
            // 居中
            .attr('text-anchor','middle')
            .attr('transform',`translate(${rectWidth/2})`)
            .text(d=>d)
    </script> -->


    <!-- <div id="app"></div>
    <style>
        #app{
            margin: 40px auto 0px;
            border: 1px solid red;
            max-width: calc(100% - 10px);
        }
    </style>
    <script>
        // 五、比例尺（线性比例尺、序数比例尺）
        // 前面的 draw 函数图像和屏幕像素是 1:1 显示的，如果数据太大、太小都没法显示
        // 可以通过比例尺把真实数据限制在一定范围

        // 比例尺有很多种，可以分为两类：
        //    线性比例尺：输入输出在连续的范围；
        //    序数比例尺：比如 输入 [1,2,3,4] 输出 ['red','green','yellow']；

        // 线性比例尺

        // 1.scaleLinear：
        // const scale1 = d3.scaleLinear().domain([0,5]).range([5,10])
        // scale1.clamp(true) // 不允许超出值域
        // console.log(scale1(1)) // 6
        // console.log(scale1(2)) // 7
        // console.log(scale1(10)) // 10

        // 2.scaleQuantile：输出是不连续的数，输入依据输出平分
        // const scale4 = d3.scaleQuantile().domain([1,10]).range(['xm','xh','xq'])
        // console.log(scale4(1)) // xm
        // console.log(scale4(2)) // xm
        // console.log(scale4(7)) // xq


        // 序数比例尺

        // 1.scaleBand：输出是一个范围
        // const scale2 = d3.scaleBand().domain([1,2,3,4,5]).range([0,100])
        // console.log(scale2(1)) // 0
        // console.log(scale2(2)) // 20 
        // console.log(scale2(4)) // 60
        // console.log(scale2(8)) // undefined

        // 2.scaleOrdinal：如果没匹配上，会在前一个匹配了的基础上选后一个
        // const scale3 = d3.scaleOrdinal().domain(['s','m','l']).range(['11','33','99'])
        // console.log(scale3('s12')) // 11
        // console.log(scale3('s')) // 11
        // console.log(scale3('s14')) // 33
        // console.log(scale3('s15')) // 99


        const  data = [12,600,3000,70,300,540,650]
        const min = d3.min(data)
        const max = d3.max(data)
        const linear = d3.scaleLinear()
            .domain([0,max]) // 定义域，设置为 0 更加合理
            .range([0,300]) // 值域
        const  width = 700
        const height = 400
        const rectStep = 40 // 间距
        const rectWidth = 30 // 宽度
        const margin = {left:20,right:20,top:20,bottom:20}
        d3.select('#app').append('svg')
            .attr('width',width)
            .attr('height',height)

        d3.select('svg').selectAll('rect')
            .data(data)
            .enter()
            .append('rect')
            .attr('x',(d,i)=>margin.left + i*rectStep)
            .attr('y',(d)=>height - linear(d) - margin.bottom )
            .attr('width',rectWidth)
            .attr('height',d=>linear(d))
            .attr('fill','lightblue')

        //  文字
        d3.select('svg').selectAll('text')
            .data(data)
            .enter('text')
            .append('text')
            .attr('fill','#333')
            .attr('font-size',18)
            .attr('x',(d,i)=>margin.left + i*rectStep)
            .attr('y',(d)=>height - linear(d) - margin.bottom - 5)
            // 居中
            .attr('text-anchor','middle')
            .attr('transform',`translate(${rectWidth/2})`)
            .text(d=>d)
    </script> -->

    <!-- 
    <script>
        // 六、坐标轴：有 axisBottom、axisLeft、axisRight、axisTop 四个生成器
        const width = 600
        const height = 300
        const margin = { left: 20, right: 20, top: 20, bottom: 20 }

        const kindData = ['数学','英语','语文','信息'] // x 轴
        const kindPixel = [margin.left,width - margin.right] // 左右范围

        const rationData = [50,90,80,68,100] // y 轴
        const rationPixel = [margin.top, height - margin.bottom] // 上下范围
        // 1.画布
        d3.select('body').append('svg')
          .attr('width',width)
          .attr('height',height)

        // 3.x 轴
        const xScale = d3.scaleBand()
            .domain(kindData)
            .rangeRound(kindPixel) // 去掉小数
        const xAxis = d3.axisBottom(xScale) // 坐标生成器
        d3.select('svg').append('g').call(xAxis)
          .attr('transform',`translate(0,${height - margin.bottom})`)
          .attr('font-size',12)
        // 4.y 轴
        const yScale = d3.scaleLinear()
            .domain([0,d3.max(rationData)])
            .range(rationPixel.reverse()) // 调换一下，不然坐标轴是从上到下递增
        const yAxis = d3.axisLeft(yScale)
        d3.select('svg').append('g').call(yAxis)
          .attr('transform',`translate(${margin.left},0)`)
          .attr('font-size',12)
        
    </script> -->

    <!-- <script>
        // 七、过渡动画：
        //    transition：创建过渡对象
        //    duration：运动时间
        //    delay：延迟时间
        //    ease：动画类型、运动曲线，比如淡入淡出等等（传入 d3.easeXXXX）
        const svg = d3.select('body').append('svg')
            .attr('width',600)
            .attr('height',300)
        const circle = svg.append('circle')
            .attr('cx',100)
            .attr('cy',100)
            .attr('r',20)
            .attr('fill','lightblue')
        
        // 初始状态
        circle.attr('cx',100).attr('cy',100)

        // 结束状态
        circle.transition()
            .duration(1000) // 运动耗时 1s
            .delay(2000) // 2秒后运动
            .ease(d3.easeBounce) // 
            .attr('cy',200)
            .attr('cx',400)


    </script> -->

    <!-- <script>
        // 八、过渡柱状图
        const width = 600
        const height = 400
        // 1.画布
        const svg = d3.select('body').append('svg')
            .attr('width',width)
            .attr('height',height)
        
        // 2.填充值
        const margin = { left: 20, right: 20, top: 20, bottom: 20 }
        const  data = [10,20,40,60,55,32,18]

        // 3.绘制坐标
        const xScale = d3.scaleBand()
          .domain(d3.range(data.length)) // 1,2...6
          .range([margin.left,width-margin.right])
          .padding(0.2) // 缝隙
        const xAxis = d3.axisBottom(xScale)
        svg.append('g').call(xAxis)
            .attr('transform',`translate(0,${height - margin.bottom})`)
        
        const yScale = d3.scaleLinear()
          .domain([d3.max(data),0])
          .rangeRound([margin.bottom,height-margin.top])
        const yAxis = d3.axisLeft(yScale)
        svg.append('g').call(yAxis)
            .attr('transform',`translate(${margin.left},0)`)

        // 4.柱状图
        const rects = svg.selectAll('rect')
         .data(data)
         .enter()
         .append('rect')
         .attr('class','rect')
         .attr('x',(d,i)=>xScale(i))
         .attr('y',d=>yScale(d))
         .attr('width',xScale.bandwidth()) // 动态获取宽度
         .attr('height',d=>yScale(0) - yScale(d)) // yScale(0)：y 当前最大
         .attr('fill','lightblue')
        
        // 5.文字
        const texts = svg.selectAll('aaa')
        .data(data)
        .enter()
        .append('text')
        .attr('class','text')
        .attr('font-size',12)
        .attr('fill','#333')
        .attr('x',(d,i)=>xScale(i))
        .attr('y',d=>yScale(d))
        .text(d=>d)
        .attr('transform',`translate(${xScale.bandwidth() / 2},-5)`)

        // 6.动画
        rects.attr('y',yScale(0)).attr('height',0)
            .transition().duration(1000)
            .delay((d,i)=>i*200)
            .ease(d3.easeBounce)
            .attr('y',d=>yScale(d))
            .attr('height',d=>yScale(0) - yScale(d))

        texts.attr('y',()=>yScale(0))
            .transition().duration(1000)
            .delay((d,i)=>i*200)
            .ease(d3.easeBounce)
            .attr('y',d=>yScale(d))
    </script> -->

    <!-- <script>
        // 九、交互操作：使用 on，事件类型和 dom 相同
        const  svg = d3.select('body').append('svg')
            .attr('width',600)
            .attr('height',400)

        const circle = svg.append('circle')
            .attr('cx',100)
            .attr('cy',100)
            .attr('r',50)
            .attr('fill','lightblue')
        
        // ev：mouse event，d：有数据时会得到对应的数据
        circle.on('click',(ev,d)=>{
            circle.attr('fill','lightgreen')
        })
        circle.on('mouseover',()=>{
            circle.attr('fill','orange')
        })
    </script> -->

</body>
</html>